package com.hbwxz.Cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicMarkableReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 修复ABA问题
 * <p>
 * ① 版本号时间戳原子引用（AtomicStampedReference）
 *
 * @author shenzw
 * @date 2023/12/20
 */
public class FixAbaProblem {

    /**
     * initialRef：初始化值；initialStamp：版本号
     */
    static AtomicStampedReference stampedReference = new AtomicStampedReference(100, 1);

    /**
     * initialRef：初始化值；initialMark：是否替换过版本
     */
    static AtomicMarkableReference markableReference = new AtomicMarkableReference(100, false);

    public static void main(String[] args) {
//        ways1();

        ways2();
    }

    /**
     * 解决 ABA问题方法② 版本号时间戳原子引用（AtomicMarkableReference）
     */
    private static void ways2() {

    }

    /**
     * 解决 ABA问题方法① 版本号时间戳原子引用（AtomicStampedReference）
     * <p>
     * 线程t3和t4拿到初始值100和版本号1,t3在t4睡眠期间将初始值修改为101然后再改回100,但是此时多了一个版本号记录了t3的操作,虽然初始值还是和t4之前拿的一致,但是版本号不一致代表有线程动过,因此执行返回false
     */
    private static void ways1() {
        new Thread(() -> {
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t 首次版本号：" + stamp);

            // 线程休眠一下
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            stampedReference.compareAndSet(100, 101, stampedReference.getStamp(), stampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t 2次版本号：" + stampedReference.getStamp());

            stampedReference.compareAndSet(101, 100, stampedReference.getStamp(), stampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t 3次版本号：" + stampedReference.getStamp());
        }, "t3").start();

        new Thread(() -> {
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t 首次版本号：" + stamp);

            // 暂停一会儿线程，获得初始值 100 和初始版本号1，故意暂停 3秒钟让 t3线程完成一次 ABA操作生产问题
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            boolean result = stampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName() + "\t 是否一致：" + result + "\t 结果 = " + stampedReference.getReference());
        }, "t4").start();
    }

}
